namespace SimpleWebServices.Data
{
    using System;
    using System.Net;
    using System.Web;
    using System.Xml;
    using System.Xml.Serialization;
    using System.Xml.XPath;
    using SimpleWebServices;
    using SimpleWebServices.Web;
    using SimpleWebServices.Web.UI.Html;
    using Xunit;

    public sealed class XmlDataObjectFacts
    {
        [Fact]
        public void typedef()
        {
            Assert.True(new ObjectExpectations<XmlDataObject>()
                .DerivesFrom<ValueObject<XmlDataObject>>()
                .IsConcreteClass()
                .IsSealed()
                .HasDefaultConstructor()
                .XmlRoot("object")
                .Implements<IXmlSerializable>()
                .Implements<IResource>()
                .Result);
        }

        [Fact]
        public void deserialize()
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml("<abc>123</abc>");

            XmlDataObject expected = new XmlDataObject
            {
                Token = Token.New(),
                Urn = "urn://xml/example",
                Created = DateTime.UtcNow.AddDays(-1),
                Modified = DateTime.UtcNow,
                Status = HttpStatusCode.ResetContent,
                Cacheability = HttpCacheability.Server,
                Expires = new TimeSpan(5, 0, 0, 0),
                ObjectType = typeof(string),
                Xml = document
            };

            string xml =
                "<object" +
                    " token='" + expected.Token + "'" +
                    " urn='" + expected.Urn + "'" +
                    " created='" + expected.Created.Value.ToXmlString() + "'" +
                    " modified='" + expected.Modified.Value.ToXmlString() + "'" +
                    " status='" + XmlConvert.ToString((int)expected.Status.Value) + "'" +
                    " cacheability='" + XmlConvert.ToString((int)expected.Cacheability.Value) + "'" +
                    " expires='" + expected.Expires + "'" +
                    " type='System.String'" +
                    ">" +
                    "<abc>123</abc>" +
                "</object>";
            XmlDataObject actual = xml.Deserialize<XmlDataObject>();

            Assert.Equal<XmlDataObject>(expected, actual);
        }

        [Fact]
        public void deserialize_whenMultipleChildren()
        {
            string xml =
                "<object>" +
                    "<abc>123</abc>" +
                    "<abc>456</abc>" +
                "</object>";

            Assert.Throws<InvalidOperationException>(() => xml.Deserialize<XmlDataObject>());
        }

        [Fact]
        public void deserializeEmpty()
        {
            XmlDataObject actual = "<object />".Deserialize<XmlDataObject>();

            Assert.NotNull(actual);
        }

        [Fact]
        public void serialize()
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml("<abc>123</abc>");

            XmlDataObject obj = new XmlDataObject
            {
                Token = Token.New(),
                Urn = "urn://xml/example",
                Created = DateTime.UtcNow.AddDays(-1),
                Modified = DateTime.UtcNow,
                Status = HttpStatusCode.ResetContent,
                Cacheability = HttpCacheability.Server,
                Expires = new TimeSpan(5, 0, 0, 0),
                ObjectType = typeof(string),
                Xml = document
            };

            XPathNavigator navigator = obj.Serialize().CreateNavigator();

            Assert.True((bool)navigator.Evaluate("1=count(/object[@type='System.String, mscorlib']/abc[text()='123'])"));
        }

        [Fact]
        public void ctor()
        {
            Assert.NotNull(new XmlDataObject());
        }

        [Fact]
        public void ctor_IRecord()
        {
            XmlDocument document = new XmlDocument();
            document.LoadXml("<test />");

            XmlDataObject expected = new XmlDataObject
            {
                Token = Token.New(),
                Urn = "urn://xml/example",
                Created = DateTime.UtcNow.AddDays(-1),
                Modified = DateTime.UtcNow,
                Status = HttpStatusCode.ResetContent,
                Cacheability = HttpCacheability.Server,
                Expires = new TimeSpan(5, 0, 0, 0),
                ObjectType = typeof(TestRecord),
                Xml = document
            };

            TestRecord obj = new TestRecord
            {
                Token = expected.Token,
                Urn = expected.Urn,
                Created = expected.Created,
                Modified = expected.Modified,
                Status = expected.Status,
                Cacheability = expected.Cacheability,
                Expires = expected.Expires
            };

            XmlDataObject actual = new XmlDataObject(obj);

            Assert.Equal<XmlDataObject>(expected, actual);
        }

        [Fact]
        public void ctor_IRecordNull()
        {
            XmlDataObject expected = new XmlDataObject();
            XmlDataObject actual = new XmlDataObject(null as IRecord);

            Assert.Equal<XmlDataObject>(expected, actual);
        }

        [Fact]
        public void prop_Cacheability()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Cacheability"))
                .IsAutoProperty<HttpCacheability?>()
                .Result);
        }

        [Fact]
        public void prop_Created()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Created"))
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Data()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Data"))
                .IsAutoProperty<DataCollection>()
                .Result);
        }

        [Fact]
        public void prop_Description()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Description"))
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Entity()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Entity"))
                .TypeIs<string>()
                .DefaultValueIs(string.Empty)
                .Result);
        }

        [Fact]
        public void prop_Etag()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Etag"))
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Expires()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Expires"))
                .IsAutoProperty<Expiration>()
                .Result);
        }

        [Fact]
        public void prop_Links()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Links"))
                .IsAutoProperty<LinkCollection>()
                .Result);
        }

        [Fact]
        public void prop_Metadata()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Metadata"))
                .IsAutoProperty<MetaCollection>()
                .Result);
        }

        [Fact]
        public void prop_Modified()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Modified"))
                .IsAutoProperty<DateTime?>()
                .Result);
        }

        [Fact]
        public void prop_Status()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Status"))
                .IsAutoProperty<HttpStatusCode?>()
                .Result);
        }

        [Fact]
        public void prop_Title()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Title"))
                .IsAutoProperty<string>()
                .Result);
        }

        [Fact]
        public void prop_Token()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Token"))
                .IsAutoProperty<Token?>()
                .Result);
        }

        [Fact]
        public void prop_Urn()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Urn"))
                .IsAutoProperty<AbsoluteUri>()
                .Result);
        }

        [Fact]
        public void prop_ObjectType()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("ObjectType"))
                .IsAutoProperty<Type>()
                .Result);
        }

        [Fact]
        public void prop_Xml()
        {
            Assert.True(new PropertyExpectations(typeof(XmlDataObject).GetProperty("Xml"))
                .IsAutoProperty<IXPathNavigable>()
                .Result);
        }

        [Fact]
        public void op_ToRecord()
        {
            TestRecord expected = new TestRecord
            {
                Token = Token.New(),
                Urn = "urn://xml/example",
                Created = DateTime.UtcNow.AddDays(-1),
                Modified = DateTime.UtcNow,
                Status = HttpStatusCode.ResetContent,
                Cacheability = HttpCacheability.Server,
                Expires = new TimeSpan(5, 0, 0, 0)
            };

            XmlDocument document = new XmlDocument();
            document.LoadXml("<test />");

            XmlDataObject obj = new XmlDataObject
            {
                Token = expected.Token,
                Urn = expected.Urn,
                Created = expected.Created,
                Modified = expected.Modified,
                Status = expected.Status,
                Cacheability = expected.Cacheability,
                Expires = expected.Expires,
                ObjectType = typeof(TestRecord),
                Xml = document
            };

            TestRecord actual = obj.ToRecord() as TestRecord;

            Assert.Equal<TestRecord>(expected, actual);
        }

        [Fact]
        public void op_ToRecord_whereTypeIsInvalid()
        {
            XmlDataObject obj = new XmlDataObject
            {
                ObjectType = typeof(string)
            };

            Assert.Throws<ArgumentOutOfRangeException>(() => obj.ToRecord());
        }

        [Fact]
        public void op_ToRecord_whereTypeIsNull()
        {
            Assert.Throws<ArgumentNullException>(() => new XmlDataObject().ToRecord());
        }

        [Fact]
        public void op_ToRecord_whereXmlIsNull()
        {
            TestRecord expected = new TestRecord
            {
                Token = Token.New(),
                Urn = "urn://xml/example",
                Created = DateTime.UtcNow.AddDays(-1),
                Modified = DateTime.UtcNow,
                Status = HttpStatusCode.ResetContent,
                Cacheability = HttpCacheability.Server,
                Expires = new TimeSpan(5, 0, 0, 0)
            };

            XmlDataObject obj = new XmlDataObject
            {
                Token = expected.Token,
                Urn = expected.Urn,
                Created = expected.Created,
                Modified = expected.Modified,
                Status = expected.Status,
                Cacheability = expected.Cacheability,
                Expires = expected.Expires,
                ObjectType = typeof(TestRecord)
            };

            TestRecord actual = obj.ToRecord() as TestRecord;

            Assert.Equal<string>(expected.Value, actual.Value);
        }

        [Fact]
        public void IXmlSerializable_op_GetSchema()
        {
            Assert.Throws<NotSupportedException>(() => (new XmlDataObject() as IXmlSerializable).GetSchema());
        }

        [Fact]
        public void IXmlSerializable_op_ReadXml_XmlReaderNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new XmlDataObject() as IXmlSerializable).ReadXml(null as XmlReader));
        }

        [Fact]
        public void IXmlSerializable_op_WriteXml_XmlWriterNull()
        {
            Assert.Throws<ArgumentNullException>(() => (new XmlDataObject() as IXmlSerializable).WriteXml(null as XmlWriter));
        }
    }
}